require 'lxp'

local oo = require "loop.base"

Xml = oo.class{
	indent = 0,		
	indented_code = ''
}

function Xml:nclTreeToCode( node, tabs )
	if node.tag == 'root' then
		return Xml:nclTreeToCode( node.adj[1], tabs )
	end
	
	
	local ret = ''
	
	for i=0, tabs-1 do 
		ret = ret .. '\t'
	end
	
	
	ret = ret .. '<' .. node.tag 
	for k, v in pairs( node.attr )  do
		if not tonumber(k) then
			ret  = ret .. ' ' .. k .. '='  .. '"' .. v .. '"'
		end
	end
	
	
	if #node.adj > 0 then
		ret = ret .. '>\n'
		for k, v in pairs( node.adj ) do
			ret = ret .. Xml:nclTreeToCode( v, tabs+1 )
		end
		for i=0, tabs-1 do 
			ret = ret .. '\t'
		end
		ret = ret .. '</' .. node.tag .. '>\n'
	else
		ret = ret .. '/>\n'
	end

	return ret
end

function Xml:buildNclTree( nclcode )
	local ncl_tree = { tag = 'root', attr = {}, adj = {} }
	local  stack = {}
	local  pt = ncl_tree
	
	local callbacks = {
		StartElement = function ( parser, _tag, _attr )
						
			new_node = {}
			
			new_node.tag = _tag
			new_node.attr = _attr
			new_node.adj = {}

			table.insert( pt.adj, new_node )		
			table.insert( stack, pt )
			
			pt = new_node
		end,
				
		EndElement = function ( parser, tag )
			pt = stack[#stack]
			table.remove( stack )
		end,
				
		Comment  = function( parse, comm )

		end
	}

	local p = lxp.new( callbacks )

	local res, msg, line, col, pos = p:parse( nclcode )
		
	p:parse()
	p:close()
	
	return ncl_tree
	
end

function Xml:indentCode( nclcode )
	ncltree = Xml:buildNclTree( nclcode )
	
	return Xml:nclTreeToCode( ncltree, 0 )
	
end

function Xml:mergeNclTreesAux( pt1, pt2, level )
	
	if level > 3 then
		local tagfromhead = {}
		tagfromhead['importedDocumentBase'] = true
		tagfromhead['ruleBase'] = true
		tagfromhead['transitionBase'] = true
		tagfromhead['regionBase'] = true
		tagfromhead['descriptorBase'] = true
		tagfromhead['connectorBase'] = true
		tagfromhead['meta'] = true
		tagfromhead['metadata'] = true
		for k, v in pairs( pt2.adj ) do
			if( tagfromhead[pt2.tag]) then
				table.insert( pt1.adj, v  )
			end
		end
		return
	end
	
	for k1, v1 in pairs( pt2.adj ) do
		
		local flag = true
		for k2, v2 in pairs( pt1.adj ) do
			if v1.tag == v2.tag then	
				if level < 4 then
					flag = false
				end
				self.mergeNclTreesAux( self, v2, v1, level+1 )
			end
		end
		
		if flag or pt1.tag == 'body' then
			table.insert( pt1.adj, v1 )
		end
	end
end

function Xml:mergeNclTrees( trees )
	local final_tree = { tag = 'root', attr = {}, adj = {} }
	
	for k, tree in pairs( trees ) do
		self.mergeNclTreesAux( self, final_tree, tree, 1 )	
	end
	
	return final_tree
end

function Xml:sortNclTree( tree )
	tag_priority = {}
	tag_priority['root'] = 0
	tag_priority['ncl'] = 1
	tag_priority['head'] = 2
	tag_priority['body'] = 3
	tag_priority['importedDocumentBase'] = 4
	tag_priority['ruleBase'] = 5
	tag_priority['transitionBase'] = 6
	tag_priority['regionBase'] = 7
	tag_priority['descriptorBase'] = 8
	tag_priority['connectorBase'] = 9
	tag_priority['meta'] = 10
	tag_priority['metadata'] = 11
	tag_priority['port'] = 12
	tag_priority['media'] = 13
	tag_priority['context'] = 14
	tag_priority['link'] = 15
	tag_priority['region'] = 16
	tag_priority['descriptor'] = 17
	
	function sortNCLNode( e1, e2 )
		if tag_priority[e1.tag] ~= nil and tag_priority[e2.tag] ~= nil then
			return tag_priority[e1.tag] <= tag_priority[e2.tag]
		else
			return false
		end
	end

	function sort( node )
		--if node.tag == 'root' then
			table.sort( node.adj, sortNCLNode )
			for k, v in pairs( node.adj ) do
				if #v > 1 then
					sort( v )
				end
			end
		--end
	end
	
	sort( tree )
	
	return tree
end


